/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * Description:
 * Author: huawei
 * Create: 2023-03-04
 */
#include <linux/types.h>
#include <linux/atomic.h>
#include <linux/mm.h>

#include "svm_log.h"
#include "svm_define.h"
#include "devmm_adapt.h"
#include "devmm_proc_info.h"
#include "svm_page_cnt_stats.h"

void devmm_page_cnt_stats_init(struct devmm_page_cnt_stats *stats)
{
    u32 page_type;

    for (page_type = 0; page_type < DEVMM_PAGE_TYPE_MAX; page_type++) {
        atomic64_set(&stats->cgroup_pg_cnt[page_type], 0);
    }

    for (page_type = 0; page_type < DEVMM_PAGE_TYPE_MAX; page_type++) {
        atomic64_set(&stats->cdm_pg_cnt[page_type], 0);
    }
}

void devmm_used_page_cnt_add(struct devmm_page_cnt_stats *stats, u32 page_type, struct page **pages, u64 page_num)
{
    u64 cgroup_pg_cnt = 0;
    u64 cdm_pg_cnt = 0;
    int i;

    for (i = 0; i < page_num; i++) {
        if (devmm_is_cdm_node(page_to_nid(pages[i]))) {
            cdm_pg_cnt++;
        } else {
            cgroup_pg_cnt++;
        }
    }

    atomic64_add((long)cdm_pg_cnt, &stats->cdm_pg_cnt[page_type]);
    atomic64_add((long)cgroup_pg_cnt, &stats->cgroup_pg_cnt[page_type]);
}

void devmm_used_page_cnt_sub(struct devmm_page_cnt_stats *stats, u32 page_type, struct page **pages, u64 page_num)
{
    u64 cgroup_pg_cnt = 0;
    u64 cdm_pg_cnt = 0;
    u64 tmp_pg_cnt;
    int i;

    for (i = 0; i < page_num; i++) {
        if (devmm_is_cdm_node(page_to_nid(pages[i]))) {
            cdm_pg_cnt++;
        } else {
            cgroup_pg_cnt++;
        }
    }

    tmp_pg_cnt = (u64)atomic64_sub_return((long)cdm_pg_cnt, &stats->cdm_pg_cnt[page_type]);
    if (tmp_pg_cnt >= UINT_MAX) {
        atomic64_set(&stats->cdm_pg_cnt[page_type], 0);
    }

    tmp_pg_cnt = (u64)atomic64_sub_return((long)cgroup_pg_cnt, &stats->cgroup_pg_cnt[page_type]);
    if (tmp_pg_cnt >= UINT_MAX) {
        atomic64_set(&stats->cgroup_pg_cnt[page_type], 0);
    }
}

u64 devmm_get_cgroup_used_page_cnt(struct devmm_page_cnt_stats *stats, u32 page_type)
{
    return (u64)atomic64_read(&stats->cgroup_pg_cnt[page_type]);
}

u64 devmm_get_cdm_used_page_cnt(struct devmm_page_cnt_stats *stats, u32 page_type)
{
    return (u64)atomic64_read(&stats->cdm_pg_cnt[page_type]);
}

void devmm_page_cnt_stats_show(struct devmm_page_cnt_stats *stats)
{
    u64 cgroup_page_cnt, cgroup_hpage_cnt, cdm_page_cnt, cdm_hpage_cnt;

    cgroup_page_cnt = (u64)atomic64_read(&stats->cgroup_pg_cnt[DEVMM_NORMAL_PAGE_TYPE]);
    cgroup_hpage_cnt = (u64)atomic64_read(&stats->cgroup_pg_cnt[DEVMM_HUGE_PAGE_TYPE]);

    cdm_page_cnt = (u64)atomic64_read(&stats->cdm_pg_cnt[DEVMM_NORMAL_PAGE_TYPE]);
    cdm_hpage_cnt = (u64)atomic64_read(&stats->cdm_pg_cnt[DEVMM_HUGE_PAGE_TYPE]);

    devmm_drv_info("Page cnt stats info. (cgroup_page_cnt=%llu; cgroup_hpage_cnt=%llu; "
        "cdm_page_cnt=%llu; cdm_hpage_cnt=%llu)\n",
        cgroup_page_cnt, cgroup_hpage_cnt, cdm_page_cnt, cdm_hpage_cnt);
}

